package club.vann.kafka.mock.util;

import club.vann.kafka.mock.common.exception.BussinessRuntimeException;
import club.vann.kafka.mock.common.model.csv.CsvAble;
import club.vann.kafka.mock.common.model.csv.CsvDataInfo;
import club.vann.kafka.mock.common.model.csv.CsvTable;
import cn.hutool.core.io.BomReader;
import cn.hutool.core.io.IoUtil;
import com.fasterxml.jackson.databind.MappingIterator;
import com.fasterxml.jackson.databind.ObjectReader;
import com.fasterxml.jackson.dataformat.csv.CsvMapper;
import com.fasterxml.jackson.dataformat.csv.CsvSchema;
import java.io.IOException;
import java.util.*;
import org.springframework.util.ObjectUtils;
import org.springframework.web.multipart.MultipartFile;

/** 提供 CSV相关操作工具方法。 */
public class CsvUtil {
  private CsvUtil() {}

  /**
   * 读取CSV文件，并将其解析成 CsvConvert 类型的数据。
   *
   * @param file 文件流
   * @return CSV文件解析后的数据
   */
  public static <T> List<T> reader(MultipartFile file, Class<T> clazz) {
    try {
      CsvMapper mapper = new CsvMapper();
      CsvSchema schema =
          mapper.schemaFor(LinkedHashMap.class).withHeader().withColumnReordering(true);
      ObjectReader reader = mapper.readerFor(LinkedHashMap.class).with(schema);
      BomReader bomReader = IoUtil.getBomReader(file.getInputStream());
      MappingIterator<LinkedHashMap<String, Object>> objectMappingIterator =
          reader.readValues(bomReader);
      List<LinkedHashMap<String, Object>> csvDataList = objectMappingIterator.readAll();
      return parse(csvDataList, clazz);
    } catch (IOException e) {
      throw new BussinessRuntimeException("解析CSV文件失败！");
    }
  }

  /**
   * 写CSV数据。
   *
   * @param csv csv格式数据信息
   * @return CSV字节数组
   */
  public static byte[] wirter(CsvTable csv) {
    if (ObjectUtils.isEmpty(csv)) {
      throw new BussinessRuntimeException("csv数据格式错误！");
    }
    List<String> headerList = csv.getHeaderList();
    List<List<String>> dataList = csv.getDataList();
    if (ObjectUtils.isEmpty(headerList)) {
      throw new BussinessRuntimeException("表头信息不能为空！");
    }
    List<String> csvDataList = new ArrayList<>();
    csvDataList.add(String.join(StringUtil.DELIMITER, headerList));
    // 解析表数据
    if (!ObjectUtils.isEmpty(dataList)) {
      dataList.forEach(
          rowDataList -> csvDataList.add(String.join(StringUtil.DELIMITER, rowDataList)));
    }
    String csvDataString = String.join(StringUtil.LINE_N, csvDataList);
    return csvWithBom(csvDataString);
  }

  /**
   * 根据表头解析行数据。
   *
   * @param headerList 表头
   * @param object 待解析对象
   * @return 行数据
   */
  public static List<String> parseWithHeader(List<String> headerList, Object object) {
    if (ObjectUtils.isEmpty(headerList) || ObjectUtils.isEmpty(object)) {
      return List.of();
    }
    List<String> rowDataList = new ArrayList<>();
    headerList.forEach(
        header -> {
          Object value = JSONUtil.getJSONValue(object, header);
          String valueString = String.valueOf(value);
          // 如果valueString中包含","，则需要转义，使用""包裹valueString
          if (valueString.contains(StringUtil.DELIMITER)) {
            valueString = "\"" + valueString + "\"";
          }
          rowDataList.add(valueString);
        });
    return rowDataList;
  }

  public static <T extends CsvAble> CsvDataInfo dataListToCsv(T t) {
    CsvDataInfo csvDataInfo = new CsvDataInfo();
    csvDataInfo.setTableName(t.tableName());

    // 解析header
    Map<String, Object> csvDataMap = t.csvDataMap();
    List<String> headList = csvDataMap.keySet().stream().toList();
    csvDataInfo.setHeader(String.join(StringUtil.DELIMITER, headList));
    // 解析数据
    List<String> rowList = new ArrayList<>(headList.size());
    for (String head : headList) {
      rowList.add(String.valueOf(csvDataMap.get(head)));
    }
    csvDataInfo.setRows(String.join(StringUtil.DELIMITER, rowList));
    return csvDataInfo;
  }

  /**
   * CSV文件添加BOM头。
   *
   * @param fileDataString 文件原始内容
   * @return 添加BOM后的字节数组
   */
  private static byte[] csvWithBom(String fileDataString) {
    byte[] fileDataByte = new byte[fileDataString.getBytes().length + 3];
    byte[] bomHeade = {(byte) 0XEF, (byte) 0XBB, (byte) 0XBF};
    System.arraycopy(bomHeade, 0, fileDataByte, 0, 3);
    System.arraycopy(
        fileDataString.getBytes(), 0, fileDataByte, 3, fileDataString.getBytes().length);
    return fileDataByte;
  }

  /**
   * 解析CSV数据。
   *
   * @param dataList csv数据列表
   * @param clazz 待实例类型
   * @return 实例列表
   * @param <T> 实例类型
   */
  private static <T> List<T> parse(List<LinkedHashMap<String, Object>> dataList, Class<T> clazz) {
    if (dataList == null || dataList.isEmpty()) {
      throw new BussinessRuntimeException("解析CSV数据时数据为空！");
    }
    // 解析表数据
    List<T> list = new ArrayList<>();
    for (LinkedHashMap<String, Object> rowMap : dataList) {
      T t = JSONUtil.parse(rowMap, clazz);
      list.add(t);
    }
    return list;
  }
}
